142. 环形链表 II

142. 环形链表 II

题目链接
代码随想录

分析

首先,对于环形指针,我们不应该用这种线性的模型来理解

而应该用下面这种模型来理解,更加合适

然后,我们需要确定是否有环,我们可以使用两个速度不相同的指针来解决这个问题,slow 指针一次只走一个元素的距离,fast 指针一次走两个元素的距离,那么这两个元素一定会在环中相遇,这是肯定的,原因很简单,因为 fast 指针比 slow 指针一次多走一个元素,只要 fast 指针移动到了 slow 指针的后面,那么由于这两个指针的相对速度是一个元素,所在在 slow 指针看来,fast 指针是在不断地追赶自己的,而且移动一次就距离就减少一个元素,也不会跳过自己,最终一定会相遇。
假设他们会相遇,那么慢指针会在环里转很多圈吗?感觉并不会,假设 slow 入环的时候,fast 刚好也转到了环的入环点(按理说此时他俩就相遇了,不过这里我们只是为了分析耗时最多的的情况),那么当 slow 走到环的中间的时候,fast 再次走到了入环点,然后当 slow 再次走到入环点的时候,fast 也再次走到入环点,slow、fast 最终相遇,我们从 slow 的角度看 fast,我们可以说一开始的时候,fast 需要追赶的距离是就是整个环的长度的,在 slow 再次走到入环点的时候,fast 可以追上 slow,而实际情况是 slow 在入环点的时候,fast 一定在环上非入环点的某个位置,所以,fast 需要追赶的距离是小于整个环的长度的,因此 fast 可以在 slow 再次走到入环点之前,追上 slow
此时,我们设置从头节点到入环点的距离为 x,从入环点到相遇点的距离为 y,从相遇点到入环点的距离为 z, N 为相遇前,fast 在环里转了多少圈

我们可以得到方程

2(x+y)=x+y+N(y+z)

我们可以求出

x=(N1)y+Nz

我们可以推到出来什么

解题

public ListNode detectCycle(ListNode head) {
    ListNode slow = head;
    ListNode fast = head;
    boolean notHasLoop = false;
    while(true){
	    // 因为链表可能没有元素,也可能只有一个元素
        if(fast==null || fast.next == null || fast.next.next==null){
            notHasLoop = true;
            break;
        }
        // 这个其实不用检查,因为fast一定会先检查一遍,此时已经返回了,但是为了对称,还是写吧
        if(slow==null || slow.next==null){
            notHasLoop = true;
            break;
        }
        fast=fast.next.next;
        slow=slow.next;
        // 先移动,再对比,因为一开始这俩指针就是相等的
        if(fast==slow){
            break;
        }
    }
    if(!notHasLoop){
        ListNode arrow = head;
        // 此时slow就在相遇点
        while(arrow!=slow){
            arrow=arrow.next;
            slow=slow.next;
        }
        return slow;
    }else{
        return null;
    }
}

相关题